package com.zaimokuza.cn.config;

import com.zaimokuza.cn.config.security.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

/**
 * SpringSecurity配置类
 *
 * @author zaimokuza
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
    @Resource
    private CustomizeAuthenticationFailureHandler customizeAuthenticationFailureHandler;
    @Resource
    private CustomizeFilterInvocationSecurityMetadataSource customizeFilterInvocationSecurityMetadataSource;
    @Resource
    private CustomizeAccessDecisionManager customizeAccessDecisionManager;
    @Resource
    private CustomizeLogoutSuccessHandler customizeLogoutSuccessHandler;
    @Resource
    private CustomizeAuthenticationEntryPoint customizeAuthenticationEntryPoint;
    @Resource
    private CustomizeAccessDeniedHandler customizeAccessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // 登录相关配置
        httpSecurity.addFilterAt(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        // 权限相关配置
        httpSecurity.authorizeRequests().anyRequest().authenticated().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                // 动态获取可访问请求地址对应的资源的角色信息
                object.setSecurityMetadataSource(customizeFilterInvocationSecurityMetadataSource);
                // 根据角色信息进行权限判断
                object.setAccessDecisionManager(customizeAccessDecisionManager);
                // 返回
                return object;
            }
        });
        // 登出相关配置
        httpSecurity.logout().logoutUrl("/user/logout").logoutSuccessHandler(customizeLogoutSuccessHandler).deleteCookies("JSESSIONID");
        // 异常相关处理
        httpSecurity.exceptionHandling().authenticationEntryPoint(customizeAuthenticationEntryPoint).accessDeniedHandler(customizeAccessDeniedHandler);
        // 关闭csrf
        httpSecurity.csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) {
        // 忽略静态资源
        web.ignoring().antMatchers("/swagger-resources", "/v3/api-docs", "/user/register",
                "/**/*.html", "/**/*.css", "/**/*.js", "/**/*.ico",
                "/**/*.json", "/**/*.map", "/**/*.png", "/**/*.jpg",
                "/**/*.jpeg"
        );
    }

    @Bean
    public CustomizeUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter() throws Exception {
        // 实例化过滤器对象
        CustomizeUsernamePasswordAuthenticationFilter authenticationFilter = new CustomizeUsernamePasswordAuthenticationFilter();
        // 登录成功处理
        authenticationFilter.setAuthenticationSuccessHandler(customizeAuthenticationSuccessHandler);
        // 登录失败处理
        authenticationFilter.setAuthenticationFailureHandler(customizeAuthenticationFailureHandler);
        // 登录路径
        authenticationFilter.setFilterProcessesUrl("/user/login");
        // 重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
        authenticationFilter.setAuthenticationManager(authenticationManagerBean());
        return authenticationFilter;
    }

}
